package tcp

import (
	"context"
	"errors"
	"net"
	"time"

	"github.com/silenceper/pool"

	context2 "lgrpc/context"
	"lgrpc/message"
	"lgrpc/serializer"
)

type Client struct {
	connPool   pool.Pool
	serializer serializer.Serializer
}

func NewClient(network, address string, options ...ClientOption) (*Client, error) {
	poolConfig := &pool.Config{
		InitialCap: 5,
		MaxCap:     20,
		MaxIdle:    5,
		Factory: func() (interface{}, error) {
			return net.DialTimeout(network, address, time.Minute)
		},
		Close: func(i interface{}) error {
			return i.(net.Conn).Close()
		},
		IdleTimeout: time.Minute,
	}

	connPool, err := pool.NewChannelPool(poolConfig)
	if err != nil {
		return nil, err
	}

	c := &Client{
		connPool:   connPool,
		serializer: serializer.JSONSerializer,
	}
	for _, opt := range options {
		opt(c)
	}

	return c, nil
}

func (c *Client) Invoke(ctx context.Context, req *message.Request) (*message.Response, error) {
	if ctx.Err() != nil {
		return nil, ctx.Err()
	}

	ch := make(chan struct{}, 1)
	defer close(ch)
	var (
		resp *message.Response
		err  error
	)
	go func() {
		resp, err = c.doInvoke(ctx, req)
		ch <- struct{}{}
	}()

	select {
	case <-ctx.Done():
		return nil, ctx.Err()
	case <-ch:
		return resp, err
	}
}

func (c *Client) doInvoke(ctx context.Context, req *message.Request) (*message.Response, error) {
	data := message.EncodeReq(req)

	resp, err := c.send(ctx, data)
	if err != nil {
		return nil, err
	}
	return message.DecodeResp(resp), nil
}

func (c *Client) Serializer() serializer.Serializer {
	return c.serializer
}

func (c *Client) send(ctx context.Context, data []byte) ([]byte, error) {
	val, err := c.connPool.Get()
	if err != nil {
		return nil, err
	}

	conn := val.(net.Conn)
	defer func() {
		_ = c.connPool.Put(val)
	}()

	if _, err := conn.Write(data); err != nil {
		return nil, err
	}

	// oneway 请求不再读取响应
	if context2.IsOneway(ctx) {
		return nil, errors.New("lgrpc: 这是一个 oneway 调用，你不应该处理任何结果")
	}

	return readConn(conn)
}
